package com.beiming.libra.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.beiming.libra.common.Assert;
import com.beiming.libra.common.constants.ValidateMessage;
import com.beiming.libra.common.enums.ErrorCode;
import com.beiming.libra.common.enums.PlatformEnum;
import com.beiming.libra.common.enums.SMSEnum;
import com.beiming.libra.common.enums.VerifyEnum;
import com.beiming.libra.common.mail.model.Email;
import com.beiming.libra.common.mail.service.IMailService;
import com.beiming.libra.common.utils.BeanConvertUtils;
import com.beiming.libra.common.utils.CommonsUtils;
import com.beiming.libra.common.utils.Encrypt;
import com.beiming.libra.dao.UserDetailMapper;
import com.beiming.libra.dao.UserMapper;
import com.beiming.libra.domain.MenuInfo;
import com.beiming.libra.domain.Role;
import com.beiming.libra.domain.User;
import com.beiming.libra.domain.UserDetail;
import com.beiming.libra.domain.dto.requestdto.EmailRequestDTO;
import com.beiming.libra.domain.dto.requestdto.LoginRequestDTO;
import com.beiming.libra.domain.dto.requestdto.MobilePhoneRequestDTO;
import com.beiming.libra.domain.dto.requestdto.NameVerifyRequestDTO;
import com.beiming.libra.domain.dto.requestdto.SMSLoginRequestDTO;
import com.beiming.libra.domain.dto.requestdto.UserRegisterCompRequestDTO;
import com.beiming.libra.domain.dto.requestdto.UserRegisterInfoRequestDTO;
import com.beiming.libra.domain.dto.requestdto.UserRegisterRequestDTO;
import com.beiming.libra.domain.dto.requestdto.UserResetPwdRequestDTO;
import com.beiming.libra.domain.dto.responsedto.LoginInfoResponseDTO;
import com.beiming.libra.redis.enums.RedisKey;
import com.beiming.libra.service.AuthRoleRelationService;
import com.beiming.libra.service.PersonalService;
import com.beiming.libra.service.RedisService;
import com.beiming.libra.service.SMSService;
import com.beiming.libra.service.UserRoleRelationService;
import com.beiming.libra.service.VerifyService;

/**
 * 
 * @author weibo
 *
 */
@Service
public class PersonalServiceImpl implements PersonalService {

  private static final int RANDOM_MIN = 100000;
  private static final int RANDOM_MAX = 900000;

  @Value("${sms.code.expireMinutes}")
  private int smsCodeExpireMinutes;

  @Resource
  private UserMapper userMapper;
  @Resource
  private UserDetailMapper userDetailMapper;
  @Resource
  private RedisService redisService;
  @Resource
  private SMSService smsService;
  @Resource
  private IMailService iMailService;
  @Resource
  private VerifyService verifyService;
  @Resource
  private AuthRoleRelationService authRoleRelationService;
  @Resource
  private UserRoleRelationService userRoleRelationService;

  @Override
  public void register(UserRegisterRequestDTO dto) {
    // 获取前台参数
    String userType = dto.getUserType().getCode();
    String phoneCode = dto.getPhoneCode();
    String mobilePhone = dto.getMobilePhone();
    String password = dto.getPassword();
    String userName = dto.getUserName();
    // copy bean
    User user = BeanConvertUtils.copyBean(dto, User.class);
    // 根据手机号和用户类型获取用户信息
    List<User> mobileUser = userMapper.selectByMobilePhoneAndType(mobilePhone, userType);
    // 判断手机号码是否已经注册
    Assert.isListEmpty(mobileUser, ErrorCode.MOBILE_EXITS);
    // 从redis获取短信验证码
    String smsCode = redisService.get(RedisKey.SMS_CODE, mobilePhone);
    // 对比redis中的短信验证码
    Assert.isTrue(phoneCode.equals(smsCode), ErrorCode.PHONE_CODE_IS_WRONG);
    // 新增用户信息
    String userId = CommonsUtils.get32BitUUID();
    user.setId(userId);
    // md5加密
    user.setPassword(Encrypt.md5(password));
    user.setUserType(userType);
    userMapper.insert(user);

    // 保存用户详情信息
    UserDetail userDetail = new UserDetail();
    userDetail.setUserName(userName);
    userDetail.setUserType(userType);
    // 根据用户id查询用户详情信息
    List<UserDetail> detailList = userDetailMapper.selectByUserId(userId);
    if (detailList.size() > 0) {
      // 已存在用户详情做更新操作
      UserDetail detail = detailList.get(0);
      userDetail.setId(detail.getId());
      userDetailMapper.updateByPrimaryKeySelective(userDetail);
    } else {
      // 不存在用户详情做新增操作
      userDetail.setId(CommonsUtils.get32BitUUID());
      userDetail.setUserId(userId);
      userDetailMapper.insert(userDetail);
    }

  }

  @Override
  public void registerForComp(UserRegisterCompRequestDTO dto) {
    // 获取前台参数
    String userType = dto.getUserType().getCode();
    String phoneCode = dto.getPhoneCode();
    String mobilePhone = dto.getMobilePhone();
    String password = dto.getPassword();
    // 企业名称
    String userName = dto.getUserName();
    // 机构代码
    String code = dto.getCode();
    // 企业负责人
    String responsibleUser = dto.getResponsibleUser();

    // copy bean
    User user = BeanConvertUtils.copyBean(dto, User.class);
    // 根据手机号和用户类型获取用户信息
    List<User> mobileUser = userMapper.selectByMobilePhoneAndType(mobilePhone, userType);
    // 判断手机号码是否已经注册
    Assert.isListEmpty(mobileUser, ErrorCode.MOBILE_EXITS);
    // 从redis获取短信验证码
    String smsCode = redisService.get(RedisKey.SMS_CODE, mobilePhone);
    // 对比redis中的短信验证码
    Assert.isTrue(phoneCode.equals(smsCode), ErrorCode.PHONE_CODE_IS_WRONG);
    // 新增用户信息
    String userId = CommonsUtils.get32BitUUID();
    user.setId(userId);
    // md5加密
    user.setPassword(Encrypt.md5(password));
    user.setUserType(userType);
    userMapper.insert(user);
    // 保存用户详情信息
    UserDetail userDetail = new UserDetail();
    userDetail.setUserName(userName);
    userDetail.setCode(code);
    userDetail.setResponsibleUser(responsibleUser);
    userDetail.setUserType(userType);
    // 根据用户id查询用户详情信息
    List<UserDetail> detailList = userDetailMapper.selectByUserId(userId);
    if (detailList.size() > 0) {
      // 已存在用户详情做更新操作
      UserDetail detail = detailList.get(0);
      userDetail.setId(detail.getId());
      userDetailMapper.updateByPrimaryKeySelective(userDetail);
    } else {
      // 不存在用户详情做新增操作
      userDetail.setId(CommonsUtils.get32BitUUID());
      userDetail.setUserId(userId);
      userDetailMapper.insert(userDetail);
    }

  }

  @Override
  public void next(UserRegisterInfoRequestDTO dto) {
    // 获取前台参数
    String userType = dto.getUserType().getCode();
    String mobilePhone = dto.getMobilePhone();
    String emailCode = dto.getEmailCode();
    String email = dto.getEmail();
    // copy bean
    UserDetail userDetail = BeanConvertUtils.copyBean(dto, UserDetail.class);
    // 用户类型需要保存到用户详情表中
    userDetail.setUserType(userType);
    // 根据手机号和用户类型获取用户信息
    List<User> userList = userMapper.selectByMobilePhoneAndType(mobilePhone, userType);
    // 判断用户是否存在
    Assert.isListNotEmpty(userList, ErrorCode.USER_NOT_EXISTS);
    // 获取redis中邮件验证码
    String emailCodeRedis = redisService.get(RedisKey.EMAIL_CODE, email);
    // 对比redis中的邮件验证码
    Assert.isTrue(emailCode.equals(emailCodeRedis), ErrorCode.EMAIL_CODE_IS_WRONG);

    User user = userList.get(0);
    String userId = user.getId();
    // 根据用户id查询用户详情信息
    List<UserDetail> detailList = userDetailMapper.selectByUserId(userId);
    if (detailList.size() > 0) {
      // 已存在用户详情做更新操作
      UserDetail detail = detailList.get(0);
      userDetail.setId(detail.getId());
      userDetailMapper.updateByPrimaryKeySelective(userDetail);
    } else {
      // 不存在用户详情做新增操作
      userDetail.setId(CommonsUtils.get32BitUUID());
      userDetail.setUserId(userId);
      userDetailMapper.insert(userDetail);
    }
  }

  @Override
  public void realNameVerify(NameVerifyRequestDTO dto) {
    // 获取前台参数
    String userType = dto.getUserType().getCode();
    String mobilePhone = dto.getMobilePhone();
    String username = dto.getUserName();
    String code = dto.getCode();
    // 根据手机号和用户类型获取用户信息
    List<User> userList = userMapper.selectByMobilePhoneAndType(mobilePhone, userType);
    // 判断用户是否存在
    Assert.isListNotEmpty(userList, ErrorCode.USER_NOT_EXISTS);
    User userInfo = userList.get(0);
    // 根据姓名和身份证进行实名认证
    boolean verify = verifyService.identityAuthentication(username, code);
    Assert.isTrue(verify, ErrorCode.NAME_IDCARD_MISMATCHING);
    String isRealName = verify ? VerifyEnum.YES.getCode() : VerifyEnum.NO.getCode();
    userInfo.setIsRealName(isRealName);
    userMapper.updateByPrimaryKeySelective(userInfo);
  }

  @Override
  public LoginInfoResponseDTO smsLogin(SMSLoginRequestDTO dto) {
    // 获取前台参数
    String smsCode = dto.getSmsCode();
    String mobilePhone = dto.getMobilePhone();
    String userType = dto.getUserType().getCode();
    PlatformEnum platform = dto.getPlatform();
    // 根据手机号(用户名)和用户类型获取用户信息
    List<User> userList = userMapper.selectByPhoneOrNameAndType(mobilePhone, userType);
    // 判断用户是否存在
    Assert.isListNotEmpty(userList, ErrorCode.USER_NOT_EXISTS);
    User user = userList.get(0);
    // 从redis中获取手机短信验证码
    String validSmsCode = redisService.get(RedisKey.SMS_CODE, mobilePhone);
    // 判断短信验证码是否为空
    Assert.isNotEmpty(validSmsCode, ErrorCode.SMS_CODE_INCORRECT);
    // 判断验证码是否与redis中值匹配
    Assert.isTrue(validSmsCode.equals(smsCode), ErrorCode.SMS_CODE_INCORRECT);
    // 获取用户菜单
    List<MenuInfo> menuInfos = authRoleRelationService.getAuthInfoByUserId(user.getId(), platform);
    // 获取用户角色
    // List<Role> roles = userRoleRelationService.getRoleInfoByUserId(user.getId());
    // 目前需求涉及到一个用户对应多个角色情况
    // 这里采用用户类型作为角色
    List<Role> roles = new ArrayList<Role>();
    Role role = new Role();
    role.setRoleCode(dto.getUserType().name());
    roles.add(role);
    // 组装dto
    LoginInfoResponseDTO loginInfo = new LoginInfoResponseDTO(user, menuInfos, roles);
    // 清除redis中的短信验证码
    redisService.delete(RedisKey.SMS_CODE, mobilePhone);
    return loginInfo;
  }

  @Override
  public LoginInfoResponseDTO login(LoginRequestDTO dto) {
    // 获取前台参数
    String password = dto.getPassword();
    String mobilePhone = dto.getLoginName();
    String userType = dto.getUserType().getCode();
    PlatformEnum platform = dto.getPlatform();
    // 密码md5加密
    password = Encrypt.md5(password);
    // 根据手机号(用户名)和用户类型获取用户信息
    List<User> userList = userMapper.selectByPhoneOrNameAndType(mobilePhone, userType);
    // 判断账号是否存在
    Assert.isListNotEmpty(userList, ErrorCode.USER_NAME_NOT_EXISTS, ValidateMessage.USER_NAME_NOT_EXISTS);
    User user = userList.get(0);
    // 判断密码是否正确
    Assert.isTrue(password.equals(user.getPassword()), ErrorCode.PASSWORD_IS_WRONG, ValidateMessage.PASSWORD_IS_WRONG);
    // 获取用户菜单
    List<MenuInfo> menuInfos = authRoleRelationService.getAuthInfoByUserId(user.getId(), platform);
    // 获取用户角色
    // List<Role> roles =
    // userRoleRelationService.getRoleInfoByUserId(user.getId());
    // 目前需求涉及到一个用户对应多个角色情况
    // 这里采用用户类型作为角色
    List<Role> roles = new ArrayList<Role>();
    Role role = new Role();
    role.setRoleCode(dto.getUserType().name());
    roles.add(role);
    // 组装dto
    LoginInfoResponseDTO loginInfo = new LoginInfoResponseDTO(user, menuInfos, roles);

    return loginInfo;

  }

  @Override
  public String getSMSCode(MobilePhoneRequestDTO dto) {
    // 获取前台参数
    String mobilePhone = dto.getMobilePhone();
    // 根据手机号获取用户信息
    List<User> userList = userMapper.selectByMobilePhone(mobilePhone);
    // 判断手机号是否存在
    Assert.isListNotEmpty(userList, ErrorCode.USER_NOT_EXISTS);

    // 检查redis是否已有验证码，如果有则直接使用，否则生成新的验证码
    String smsCode = redisService.get(RedisKey.SMS_CODE, mobilePhone);
    if (StringUtils.isBlank(smsCode)) {
      smsCode = generateSMSCode();
      redisService.set(RedisKey.SMS_CODE, mobilePhone, smsCode, smsCodeExpireMinutes, TimeUnit.MINUTES);
    }
    Map<String, String> data = new HashMap<>();
    data.put("smsCode", smsCode);
    // 短信发送接口
    smsService.send(mobilePhone, SMSEnum.SMS_LOGIN, data);
    return smsCode;
  }

  @Override
  public String getPhoneCode(MobilePhoneRequestDTO dto) {
    // 获取前台参数
    String mobilePhone = dto.getMobilePhone();
    // 检查redis是否已有验证码，如果有则直接使用，否则生成新的验证码
    String smsCode = redisService.get(RedisKey.SMS_CODE, mobilePhone);
    if (StringUtils.isBlank(smsCode)) {
      smsCode = generateSMSCode();
      redisService.set(RedisKey.SMS_CODE, mobilePhone, smsCode, smsCodeExpireMinutes, TimeUnit.MINUTES);
    }
    Map<String, String> data = new HashMap<>();
    data.put("smsCode", smsCode);
    // 短信发送接口
    smsService.send(mobilePhone, SMSEnum.SMS_LOGIN, data);
    return smsCode;
  }

  @Override
  public String getEmailCode(EmailRequestDTO dto) {
    String email = dto.getEmail();
    // 检查redis是否已有验证码，如果有则直接使用，否则生成新的验证码
    String emailCode = redisService.get(RedisKey.EMAIL_CODE, email);
    if (StringUtils.isBlank(emailCode)) {
      emailCode = generateSMSCode();
      redisService.set(RedisKey.EMAIL_CODE, email, emailCode, smsCodeExpireMinutes, TimeUnit.MINUTES);
    }
    Email emailObject = new Email();
    String[] emailArray = { email };
    // 邮箱
    emailObject.setEmail(emailArray);
    // 主题
    emailObject.setSubject("智慧仲裁注册邮件验证码");
    // 内容
    emailObject.setContent(emailCode);
    iMailService.send(emailObject);
    return emailCode;
  }

  @Override
  public void resetPwd(UserResetPwdRequestDTO dto) {

    // 获取前台参数
    String userType = dto.getUserType().getCode();
    String phoneCode = dto.getPhoneCode();
    String mobilePhone = dto.getMobilePhone();
    String password = dto.getPassword();

    User user = new User();
    // 根据手机号和用户类型获取用户信息
    List<User> mobileUser = userMapper.selectByMobilePhoneAndType(mobilePhone, userType);
    // 判断用户是否存在
    Assert.isListNotEmpty(mobileUser, ErrorCode.USER_NOT_EXISTS);
    // 从redis获取短信验证码
    String smsCode = redisService.get(RedisKey.SMS_CODE, mobilePhone);
    // 对比redis中的短信验证码
    Assert.isTrue(phoneCode.equals(smsCode), ErrorCode.PHONE_CODE_IS_WRONG);

    User userInfo = mobileUser.get(0);
    user.setId(userInfo.getId());
    // md5加密
    user.setPassword(Encrypt.md5(password));
    userMapper.updateByPrimaryKeySelective(user);

  }

  /**
   * 验证码规则：6位有效数字
   * 
   * @return
   */
  private String generateSMSCode() {
    Random r = new Random();
    int code = RANDOM_MIN + r.nextInt(RANDOM_MAX);
    return String.valueOf(code);
  }
}
